package top.guoziyang.mydb.backend.server;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import top.guoziyang.mydb.backend.tbm.TableManager;
import top.guoziyang.mydb.transport.Encoder;
import top.guoziyang.mydb.transport.Package;
import top.guoziyang.mydb.transport.Packager;
import top.guoziyang.mydb.transport.Transporter;

/**
 * Server 类实现了一个简单的服务器，负责监听指定端口，接受客户端的连接请求，并使用线程池处理每个连接
 */
public class Server {
    private int port;
    TableManager tbm;

    public Server(int port, TableManager tbm) {
        this.port = port;
        this.tbm = tbm;
    }

    /**
     * 启动服务器，监听指定端口
     */
    public void start() {
        ServerSocket ss = null;
        try {
            ss = new ServerSocket(port);
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
        System.out.println("Server listen to port: " + port);
        // 初始化一个线程池，用于并发处理客户端连接:核心线程数：10,最大线程数：20,空闲线程存活时间：1秒,工作队列：ArrayBlockingQueue，容量为 100,拒绝策略：CallerRunsPolicy（当线程池满时，直接在调用线程中运行任务）
        ThreadPoolExecutor tpe = new ThreadPoolExecutor(10, 20, 1L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(100), new ThreadPoolExecutor.CallerRunsPolicy());
        try {
            while(true) { //循环等待客户端连接，并为每个连接分配任务
                Socket socket = ss.accept(); // 等待客户端连接
                Runnable worker = new HandleSocket(socket, tbm); // 创建任务
                tpe.execute(worker);   // 将任务提交给线程池
            }
        } catch(IOException e) {
            e.printStackTrace();
        } finally {
            try {
                ss.close();
            } catch (IOException ignored) {}
        }
    }
}

/**
 * HandleSocket 类实现了 Runnable 接口，用于处理单个客户端的连接请求。它负责接收客户端发送的 SQL 请求，调用执行器处理请求，返回结果给客户端。
 */
class HandleSocket implements Runnable {
    private Socket socket;
    private TableManager tbm;

    public HandleSocket(Socket socket, TableManager tbm) {
        this.socket = socket;
        this.tbm = tbm;
    }

    /**
     * 执行任务
     */
    @Override
    public void run() {
        InetSocketAddress address = (InetSocketAddress)socket.getRemoteSocketAddress();
        System.out.println("Establish connection: " + address.getAddress().getHostAddress()+":"+address.getPort()); //打印记录客户端的 IP 地址和端口号
        Packager packager = null;
        try {
            Transporter t = new Transporter(socket);
            Encoder e = new Encoder();
            packager = new Packager(t, e);
        } catch(IOException e) {
            e.printStackTrace();
            try {
                socket.close();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
            return;
        }
        // 执行SQL请求
        Executor exe = new Executor(tbm);
        while(true) {
            Package pkg = null;
            try {
                pkg = packager.receive();
            } catch(Exception e) {
                break;
            }
            byte[] sql = pkg.getData(); // 获取 SQL 数据
            byte[] result = null;
            Exception e = null;
            try {
                result = exe.execute(sql);  // 执行 SQL 请求
            } catch (Exception e1) {
                e = e1;
                e.printStackTrace();
            }
            pkg = new Package(result, e); // 封装执行结果或异常
            try {
                packager.send(pkg); // 发送结果给客户端
            } catch (Exception e1) {
                e1.printStackTrace();
                break;
            }
        }
        exe.close();
        try {
            packager.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
}